/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package au.edu.swin.bank.ejb.session.stateless;

import au.edu.swin.bank.persistence.entity.Account;
import au.edu.swin.bank.persistence.entity.Bill;
import au.edu.swin.bank.persistence.entity.BillDTO;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.PersistenceContext;
import javax.ejb.Remote;
import javax.ejb.Local;
import au.edu.swin.bank.persistence.entity.Customer;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import javax.ejb.EJB;
import javax.persistence.NoResultException;

/**
 *
 * @author root
 */
@Stateless
@Remote(CustomerRemote.class)
@Local(CustomerLocal.class)
public class CustomerBean implements CustomerRemote, CustomerLocal
{
    @PersistenceContext
    private EntityManager em;

    @EJB
    AccountLocal accountBean;
    
    /**
     * Performs a login of a customer/staff member with the supplied username and password
     * @param aUsername The username of the customer/staff member
     * @param aPassword The password of the customer/staff member
     * @return The customer object if login details are correct, otherwise null
     */
    private Customer handleLogin(int custId, int aPassword){
        try{
            //print out the login details to the GlassFish output for debugging and verification purposes
            System.out.println("Trying to login as: " + custId + " with password " + aPassword);
            System.err.println("Trying to login as: " + custId + " with password " + aPassword);

            //get for the customer
            Customer customer = getCustomer(custId);
            
            System.err.println("Trying to login: " + customer );
            if (customer == null)
                return null;

            //check if password supplied is correct, if not, display an error
            if(customer.getPassword() == aPassword)
                return customer;
            else
                System.err.println("Password didn't match " + aPassword + " cust: " + customer.getPassword());
        }
        catch(Exception e){
            e.printStackTrace();
            return null;
        }
        return null;
    }

    /**
     * Performs a login
     * @param aCustId The username of the customer/staff member
     * @param aPassword The password of the customer/staff member
     * @return True if the customer is a staff member, otherwise false
     */
    public boolean staffLogin(int aCustId, int aPassword){
        Customer cust = handleLogin(aCustId, aPassword);
        return cust == null ? false : cust.getStaffmember().intValue() != 0;
    }

    /**
     * Performs a login
     * @param aCustId The username of the customer/staff member
     * @param aPassword The password of the customer/staff member
     * @return True if the customer is not a staff member, otherwise false
     */
    public boolean customerLogin(int aCustId, int aPassword){
        //Load the customer record, returning false if they are not found or are staff
        Customer cust = handleLogin(aCustId, aPassword);
        return cust == null ? false : (cust.getStaffmember().intValue() == 0 && cust.getAccountCollection().size() > 0);
    }

    /**
     * Performs a login
     * @param aCustId The username of the customer/staff member
     * @param aPassword The password of the customer/staff member
     * @return True if the login succeeds, otherwise false
     */
    public boolean login(int aCustId, int aPassword){
        return handleLogin(aCustId, aPassword) == null ? false : true;
    }

    /**
     * Gets the ID numbers of the accounts owned by a customer.
     *
     * @param custId
     *
     * @return the IDs of the customer's accounts
     */
    public Collection<BigInteger> getAccountIds(int aCustId)
    {
        Collection<BigInteger> accountIds = new HashSet<BigInteger>();
        
        em.getEntityManagerFactory().getCache().evictAll();

        //Get the customer
        Customer customer = getCustomer(aCustId);

        if (customer == null)
            return accountIds;
        else
        {
            Collection<Account> accounts = customer.getAccountCollection();
            Iterator iterator = accounts.iterator();

            //Add them all to a list and return the list
            while(iterator.hasNext()) {
                Account account = (Account)iterator.next();
                accountIds.add(account.getAccountId());
            }
        }
        
        return accountIds;
    }

    /**
     * Gets the bills linked to a customer.
     *
     * @param custId
     * @param onlyUnpaidBills if true, only unpaid bills are returned
     *
     * @return returns the bills linked to a customer
     */
    public Collection<BillDTO> getBills(int aCustId, boolean onlyUnpaidBills)
    {
        Collection<BillDTO> bills = new ArrayList<BillDTO>();
        
        em.getEntityManagerFactory().getCache().evictAll();

        //Get the customer
        Customer customer = getCustomer(aCustId);
        if (customer == null)
            return bills;
        else
        {
            for (Bill bill : customer.getBillCollection())
            {
                if (!onlyUnpaidBills || Double.compare(bill.getAmount(), bill.getAmountPaid()) > 0)
                {
                    BigInteger billId = bill.getBillId();
                    double amount = bill.getAmount();
                    double amountPaid = bill.getAmountPaid();
                    BigInteger billerAccountId = bill.getBiller().getAccount().getAccountId();
                    String billerName = bill.getBiller().getName();
                    bills.add(new BillDTO(billId, amount, amountPaid, aCustId, billerAccountId, billerName));
                }
            }
        }
        return bills;
    }

    /**
     * Checks whether a customer with the specified ID is a staff member.
     *
     * @param custId
     *
     * @return true if the customer is a staff member, false if otherwise
     */
    public boolean isStaff(int aCustId) {
        try {
            //Get the customer
            Customer customer = getCustomer(aCustId);

            if (customer == null)
                return false;
            else
                return customer.getStaffmember().equals(1);
        } catch (NoResultException e) {
            return false;
        }
    }

    /**
     * Opens a new account for a customer with the specified ID. If no customer
     * with the specified ID exists, a new customer with that ID will be created
     * along with its own bank account.
     *
     * @param aCustId
     *
     * @return true if the account opening was successful, false if otherwise
     */
    public boolean openAccount(int aCustId) {
        try {
            //Get the customer
            Customer customer = getCustomer(aCustId);

            if (customer == null) {
                
                Customer newCustomer = new Customer();
                
                newCustomer.setCustId(aCustId);
                newCustomer.setStaffmember(0);
                newCustomer.setPassword(1234); //Creates a customer with a generic password
                persist(newCustomer);
                em.flush();
                em.refresh(newCustomer);
                Account account = new Account();
                account.setCustomer(newCustomer);
                account.setBalance(0.00);
                account.setRemoved(false);
                persist(account);
                return true;
            }
            else
            {
                Account account = new Account();
                account.setCustomer(customer);
                account.setBalance(0.00);
                account.setRemoved(false);
                persist(account);
                return true;
            }
        } catch (NoResultException e) {
            return false;
        }
    }

    /**
     * Closes an account with the specified ID.
     *
     * @param aAccountId
     *
     * @return true if the account closure was successful, false if otherwise
     */
    public boolean closeAccount(BigInteger aAccountId) {
        try {
            //Get the customer account
            Account account = accountBean.getAccount(aAccountId);
            if (account == null) {
                return false;
            }
            if (account.getBalance() != 0.00) {
                return false;
            }
            account.setRemoved(true);
            persist(account);
            return true;
        } catch (NoResultException e) {
            return false;
        }
    }

    /**
     * Checks whether a customer with the specified ID exists.
     *
     * @param custId
     *
     * @return true if the customer exists, false if otherwise
     */
    public boolean customerExists(int custId)
    {
        return getCustomer(custId) != null;
    }

    /**
     * Gets a Customer with the specified ID.
     *
     * @param custId
     *
     * @return the Customer object if the customer with the specified ID exists; null if otherwise
     */
    private Customer getCustomer(int custId)
    {
        Query query = em.createNamedQuery("Customer.findByCustid").setParameter("custid", custId);
        try
        {
            Customer cust = (Customer) query.getSingleResult();
            return cust;
        }
        catch (NoResultException e)
        {
            return null;
        }
    }

    /**
     * Persists the object to the database.
     * @param object
     */
    public void persist(Object object)
    {
        em.persist(object);
    }
    
    /**
     * Forces an eviction of the data in the Bean's cache.
     */
    public void forceEvict(){
        em.getEntityManagerFactory().getCache().evictAll();
    }
}
